﻿#region NeoPatterns
// 
// NeoPatterns framework is released under LGPLv3 license.
// See http://www.opensource.org/licenses/lgpl-3.0.html for more details.
// 
// Please visit http://neopatterns.wikidot.com for more information about NeoPatterns
// 
// Copyright (c) Pascal Craponne 2008
// 
#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

namespace NeoPatterns.Patterns.Adapter
{
    public class ListAdapter : IArrayAdapter
    {
        /// <summary>
        /// The IList/IList&lt;> implementation to be wrapped
        /// </summary>
        private object list;

        private MethodInfo getCountMethod;
        private MethodInfo getAtMethod;
        private MethodInfo setAtMethod;

        /// <summary>
        /// The wrapped array, here an IList implementation
        /// </summary>
        public object Array
        {
            get { return list; }
            set
            {
                list = value;
                var ilist = list.GetType().GetInterface(typeof(IList).Name);
                if (ilist != null)
                    SetMethodsFromIList(ilist);
                else
                {
                    var genericIList = list.GetType().GetInterface(typeof(IList<>).Name);
                    if (genericIList != null)
                        SetMethodsFromIList(genericIList);
                }
            }
        }

        /// <summary>
        /// Gives number of elements present in list
        /// </summary>
        public int Count
        {
            get
            {
                return (int)getCountMethod.Invoke(list, null);
            }
        }

        /// <summary>
        /// Gets or sets entry in the wrapped array
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public object this[int index]
        {
            get
            {
                return getAtMethod.Invoke(list, new object[] { index });
            }
            set
            {
                setAtMethod.Invoke(list, new[] { index, value });
            }
        }

        /// <summary>
        /// Returns the PropertyInfo with given name from a parent interface
        /// (apparently, you can't flatten hierarchy for interfaces)
        /// </summary>
        /// <param name="interfaceType"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        protected static PropertyInfo GetInterfaceProperty(Type interfaceType, string name)
        {
            var property = interfaceType.GetProperty(name);
            if (property != null)
                return property;
            foreach (var parentInterfaceType in interfaceType.GetInterfaces())
            {
                property = parentInterfaceType.GetProperty(name);
                if (property != null)
                    return property;
            }
            return null;
        }

        /// <summary>
        /// Fills array wrapped methods
        /// </summary>
        /// <param name="listType"></param>
        protected void SetMethodsFromIList(Type listType)
        {
            getCountMethod = GetInterfaceProperty(listType, "Count").GetGetMethod();
            getAtMethod = GetInterfaceProperty(listType, "Item").GetGetMethod();
            setAtMethod = GetInterfaceProperty(listType, "Item").GetSetMethod();
        }
    }
}
